home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / Calendar / Calendar.m < prev    next >
Text File  |  1995-06-12  |  13KB  |  537 lines

  1. //----------------------------------------------------------------------------------------------------
  2. //
  3. //    Calendar
  4. //
  5. //    Inherits From:        Control
  6. //
  7. //    Declared In:        Calendar.h
  8. //
  9. //    Disclaimer
  10. //
  11. //        You may freely copy, distribute and reuse this software and its
  12. //        associated documentation. I disclaim any warranty of any kind, 
  13. //        expressed or implied, as to its fitness for any particular use.
  14. //
  15. //----------------------------------------------------------------------------------------------------
  16. #import "Calendar.h"
  17.  
  18.  
  19. static const char*  DAYS [7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",     "Friday", "Saturday" };
  20.  
  21. static const char*  MONTHS [12] = { "January", "February", "March", "April", "May", "June", "July",     "August", "September", "October", "November", "December" };
  22.  
  23. static int    LAST_DAY_OF_MONTH [13] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  24.     
  25. char     dateString [25];
  26.  
  27.  
  28.  
  29. @implementation Calendar
  30.  
  31. //----------------------------------------------------------------------------------------------------
  32. //    Private Methods
  33. //----------------------------------------------------------------------------------------------------
  34. - _initMonthTextField
  35. {
  36.     //  Create the monthTextField instance and add it as a subview.
  37.     
  38.     Font*    font = [Font newFont:"Helvetica-Bold" size:12 style:0 matrix:NX_FLIPPEDMATRIX];
  39.     NXRect    frameRect = {5.0, 114.0, 94.0, 15.0};
  40.  
  41.     monthTextField = [[TextField allocFromZone:[self zone]] initFrame: &frameRect];
  42.     [monthTextField setEditable: NO];
  43.     [monthTextField setBezeled: NO];
  44.     [monthTextField setBackgroundColor: NX_COLORLTGRAY];
  45.     [monthTextField setFont: font];
  46.     [self addSubview: monthTextField];
  47.     return self;
  48. }
  49.  
  50.  
  51. - _initYearTextField
  52. {
  53.     //  Create the yearTextField instance and add it as a subview.
  54.     
  55.     Font*    font = [Font newFont:"Helvetica-Bold" size:12 style:0 matrix:NX_FLIPPEDMATRIX];
  56.     NXRect    frameRect = {133.0, 114.0, 33.0, 15.0};
  57.  
  58.     yearTextField = [[TextField allocFromZone:[self zone]] initFrame: &frameRect];
  59.     [yearTextField setEditable: NO];
  60.     [yearTextField setBezeled: NO];
  61.     [yearTextField setAlignment: NX_RIGHTALIGNED];
  62.     [yearTextField setBackgroundColor: NX_COLORLTGRAY];
  63.     [yearTextField setFont: font];
  64.     [self addSubview: yearTextField];
  65.     return self;
  66. }
  67.  
  68.  
  69. - _initWeekdayTextField
  70. {
  71.     //  Create the weekdayTextField instance and add it as a subview.
  72.     
  73.     Font*    font = [Font newFont:"Helvetica-Bold" size:10 style:0 matrix:NX_FLIPPEDMATRIX];
  74.     NXRect    frameRect = {7.0, 96.0, 157.0, 13.0};
  75.  
  76.     weekdayTextField = [[TextField allocFromZone:[self zone]] initFrame: &frameRect];
  77.     [weekdayTextField setEditable: NO];
  78.     [weekdayTextField setBezeled: NO];
  79.     [weekdayTextField setAlignment: NX_CENTERED];
  80.     [weekdayTextField setBackgroundColor: NX_COLORDKGRAY];
  81.     [weekdayTextField setTextColor: NX_COLORWHITE];
  82.     [weekdayTextField setFont: font];
  83.     [weekdayTextField setStringValue: " S    M     T     W     T     F     S"];
  84.     [self addSubview: weekdayTextField];
  85.     return self;
  86. }
  87.  
  88.  
  89. - _initDayMatrix
  90. {
  91.     //  Create the dayMatrix ButtonCell Matrix and add it as a subview.
  92.     
  93.     id        buttonCell;
  94.     int        cellTag = 0;
  95.     int        row;
  96.     int        column;
  97.     Font*    font = [Font newFont:"Helvetica" size:10 style:0 matrix:NX_FLIPPEDMATRIX];
  98.     NXRect     matrixRect = {7.0, 3.0, 161.0, 90.0};
  99.     NXRect      cellRect = {0.0, 0.0, 23.0, 15.0};
  100.     NXSize      cellSize = {0.0, 0.0};
  101.  
  102.     buttonCell = [[ButtonCell allocFromZone:[self zone]] initTextCell: ""];
  103.     [buttonCell setHighlightsBy: NX_CHANGEGRAY];
  104.     [buttonCell setType: NX_ONOFF];
  105.     [buttonCell setEditable: NO];
  106.     [buttonCell setBordered: NO];
  107.     [buttonCell setFont: font];
  108.  
  109.     dayMatrix = [[Matrix  allocFromZone:[self zone]] initFrame:&matrixRect         mode:NX_RADIOMODE prototype:buttonCell numRows:6 numCols:7];
  110.         
  111.     for (row = 0; row < 6; row++)
  112.         for (column = 0; column < 7; column++)
  113.             [[dayMatrix cellAt:row :column] setTag: cellTag++];
  114.  
  115.     [dayMatrix setIntercell:&cellSize];
  116.     [dayMatrix setCellSize:&cellRect.size];
  117.     [dayMatrix sizeToCells];
  118.     [dayMatrix setAutodisplay:YES];
  119.     [[dayMatrix setTarget: self] setAction: @selector (_daySelected:)];
  120.     [dayMatrix setEmptySelectionEnabled: YES];
  121.     [self addSubview: dayMatrix];
  122.     return self;
  123. }
  124.  
  125.  
  126. - _initWithSystemDate
  127. {
  128.         struct  timeval greenwich;
  129.     struct  tm*    today;
  130.         
  131.         gettimeofday (&greenwich, NULL);
  132.         today = (localtime (&(greenwich.tv_sec)));
  133.     day = today->tm_mday;
  134.     month = 1 + today->tm_mon;
  135.     year = 1900 + today->tm_year;
  136.     return self;
  137. }
  138.  
  139.  
  140. - (int) _lastDayOfMonth
  141. {
  142.     if (month == 0) [self _initWithSystemDate];
  143.     if (month == 2) LAST_DAY_OF_MONTH[1] = (year % 4 ? 28 : 29);
  144.     return LAST_DAY_OF_MONTH [month - 1];
  145. }
  146.  
  147.  
  148. - (int) _firstWeekdayOfMonth
  149. {
  150.     int        theMonth = month;
  151.     int        theYear = year;
  152.     int        weekDay;
  153.     int         offset;
  154.     long     julian;
  155.  
  156.     if (theMonth <= 2) { theYear--; theMonth += 12; }
  157.  
  158.     offset = 0;
  159.     if ((theYear * 10000.0) + (theMonth * 100.0) + 1 >= 15821015.0)
  160.         offset = (2 - (theYear / 100)) + ((theYear / 100) / 4);
  161.  
  162.     julian = (365.25 * theYear);
  163.     julian += (long) (30.6001 * (theMonth +1));
  164.     julian += (long) 1;
  165.     julian += 1720994L;
  166.     julian += (long) offset;
  167.  
  168.     weekDay = (int) ((julian + 2) % 7);
  169.     return (weekDay < 0 || weekDay > 6 ? 0 : weekDay);
  170. }
  171.  
  172.  
  173. - _highlightCurrentDay
  174. {
  175.     //  Called by _updateCalendar and day: methods to select current day.
  176.     
  177.     int    firstWeekday;
  178.     id    selectedCell;
  179.  
  180.     [dayMatrix selectCellAt: -1 :-1];
  181.     if (day == 0) return self;
  182.     
  183.     firstWeekday = [self _firstWeekdayOfMonth];
  184.     selectedCell = [dayMatrix findCellWithTag: day + firstWeekday - 1];
  185.  
  186.     if ([selectedCell title]) 
  187.         if (strcmp ([selectedCell title], "")) 
  188.             [dayMatrix selectCellWithTag: day + firstWeekday - 1];
  189.         
  190.     return self;
  191. }
  192.  
  193.  
  194. - _sendActionToTarget
  195. {
  196.     if ([self target] && [self isEnabled])
  197.         if ([[self target] respondsTo: [self action]])
  198.             [[self target] perform:[self action] with:self];
  199.             
  200.     return self;
  201. }
  202.  
  203.  
  204. - _daySelected: sender
  205. {
  206.     //  Private action method for dayMatrix.  Send user's action to target.
  207.     
  208.     day = ([sender selectedCell] ? atoi ([[sender selectedCell] title]) : 0);
  209.     [self _sendActionToTarget];
  210.     return self;
  211. }
  212.  
  213.  
  214. - _updateCalendar
  215. {
  216.     //  Handles display update of subviews.
  217.     
  218.     int        date;
  219.     int        cellTag;
  220.     int        firstWeekday = [self _firstWeekdayOfMonth];
  221.     int        lastDayOfMonth = [self _lastDayOfMonth];
  222.     char        dateString[3];
  223.     
  224.     [[dayMatrix window] disableDisplay];
  225.     
  226.     for (cellTag = 0; cellTag < firstWeekday; cellTag++)
  227.         [[[dayMatrix findCellWithTag: cellTag] setTitle: ""] setEnabled: NO];
  228.         
  229.     for (cellTag = lastDayOfMonth; cellTag < [dayMatrix cellCount]; cellTag++)
  230.         [[[dayMatrix findCellWithTag: cellTag] setTitle: ""] setEnabled: NO];
  231.         
  232.     for (cellTag = firstWeekday, date = 1; date <= lastDayOfMonth; cellTag++, date++)
  233.         {
  234.         sprintf (dateString, "%d", date);
  235.         [[[dayMatrix findCellWithTag: cellTag] setTitle: dateString] setEnabled: isEnabled];
  236.         }
  237.         
  238.     [self _highlightCurrentDay];
  239.     [monthTextField setStringValue: MONTHS [month - 1]];
  240.     [yearTextField setIntValue: year];
  241.  
  242.     [[dayMatrix window] reenableDisplay];
  243.     [self display];
  244.     return self;
  245. }
  246.     
  247.     
  248. - _nextText: sender
  249. {
  250.     //  Called by 'takeFrom' methods to assist UI flow.
  251.     
  252.     if ([sender isKindOf: [TextField class]]) 
  253.         if ([[sender nextText] respondsTo: @selector(selectText:)])
  254.             [[sender nextText] selectText:nil];
  255.  
  256.     return self;
  257. }
  258.  
  259.  
  260. //----------------------------------------------------------------------------------------------------
  261. //    Initializing and Freeing
  262. //----------------------------------------------------------------------------------------------------
  263. - initFrame: (const NXRect*) frameRect
  264. {
  265.     //  Init and display current date.  Create an ActionCell instance to
  266.     //  hold our target/action values. Enable day selection.
  267.     
  268.     [super initFrame: frameRect];
  269.     [self setCell: [[ActionCell allocFromZone:[self zone]] init]];
  270.     [self setEnabled: YES];
  271.     [self _initMonthTextField];
  272.     [self _initYearTextField];
  273.     [self _initWeekdayTextField];
  274.     [self _initDayMatrix];
  275.     [self _initWithSystemDate];
  276.     [self _updateCalendar];
  277.     return self;
  278. }
  279.  
  280.  
  281. - free
  282. {
  283.     if (dayMatrix) [dayMatrix free];
  284.     if (monthTextField) [monthTextField free];
  285.     if (yearTextField) [yearTextField free];
  286.     if (weekdayTextField) [weekdayTextField free];
  287.     return [super free];
  288. }
  289.  
  290.  
  291. //----------------------------------------------------------------------------------------------------
  292. //    Accessors
  293. //----------------------------------------------------------------------------------------------------
  294. - month: (int) aMonth
  295. {
  296.     month = (aMonth < 1 || aMonth > 12 ? 1 : aMonth);
  297.     [self _updateCalendar];
  298.     return self;
  299. }
  300.  
  301.  
  302. - day: (int) aDay
  303. {
  304.     day = (aDay < 1 || aDay > [self _lastDayOfMonth] ? 0 : aDay);
  305.     [self _highlightCurrentDay];
  306.     return self;
  307. }
  308.  
  309.  
  310. - year: (int) aYear
  311. {
  312.     year = (aYear < 1 || aYear > 9999 ? 1 : aYear);
  313.     [self _updateCalendar];
  314.     return self;
  315. }
  316.  
  317.  
  318. - (const char*) monthStringValue
  319. {
  320.     return [monthTextField stringValue];
  321. }
  322.  
  323.  
  324. - (const char*) dayStringValue
  325. {
  326.     return ([[dayMatrix selectedCell] title]);
  327. }
  328.  
  329.  
  330. - (const char*) yearStringValue
  331. {
  332.     return [yearTextField stringValue];
  333. }
  334.  
  335.  
  336. - (const char*) dayOfWeekStringValue
  337. {
  338.     return DAYS [([[dayMatrix selectedCell] tag] % 7)];
  339. }
  340.  
  341.  
  342. - (const char*) dateStringValue
  343. {
  344.     if (day) 
  345.         sprintf (dateString, "%s %d, %.4d", [self monthStringValue], day, [self yearIntValue]);
  346.     else
  347.         sprintf (dateString, "%s %.4d", [self monthStringValue], [self yearIntValue]);
  348.  
  349.     return dateString;
  350. }
  351.  
  352.  
  353. - (int) monthIntValue
  354. {
  355.     return month;
  356. }
  357.     
  358.     
  359. - (int) dayIntValue
  360. {
  361.     return day;
  362. }
  363.  
  364.  
  365. - (int) yearIntValue
  366. {
  367.     return year;
  368. }
  369.  
  370.  
  371. - (int) dayOfWeekIntValue
  372. {
  373.     return ([[dayMatrix selectedCell] tag] % 7);
  374. }
  375.  
  376.  
  377. - (const char*) stringValue
  378. {
  379.     return [self dateStringValue];
  380. }
  381.  
  382.  
  383. - (int) intValue
  384. {
  385.     return day;
  386. }
  387.  
  388.  
  389. - (float) floatValue
  390. {
  391.     return (float) day;
  392. }
  393.  
  394.  
  395. - (double) doubleValue
  396. {
  397.     return (double) day;
  398. }
  399.  
  400.  
  401. //----------------------------------------------------------------------------------------------------
  402. //    Enabling Day Selection
  403. //----------------------------------------------------------------------------------------------------
  404. - setEnabled: (BOOL) aFlag
  405. {
  406.     //  Set enabling of day selection to aFlag and redisplay calendar (this will 
  407.     //  force update of dayMatrix buttonCells which are what actually gets
  408.     //  enabled or disabled).  Since the subview dayMatrix handles the mouse 
  409.     //  events we're interested in, we (the superview) will always remain disabled.
  410.     
  411.     isEnabled = aFlag;
  412.     [self _updateCalendar];
  413.     [super setEnabled: NO];
  414.     return self;
  415. }
  416.  
  417.  
  418. - (BOOL) isEnabled
  419. {
  420.     return isEnabled;
  421. }
  422.  
  423.  
  424. //----------------------------------------------------------------------------------------------------
  425. //    Action Methods
  426. //----------------------------------------------------------------------------------------------------
  427. - incrementMonth: sender
  428. {
  429.     if (++month > 12) month = 1;
  430.     [self _updateCalendar];
  431.     return self;
  432. }
  433.  
  434.  
  435. - decrementMonth: sender
  436. {
  437.     if (--month < 1) month = 12;
  438.     [self _updateCalendar];
  439.     return self;
  440. }
  441.  
  442.  
  443. - incrementYear: sender
  444. {
  445.     if (++year > 9999) year = 1;
  446.     [self _updateCalendar];
  447.     return self;
  448. }
  449.  
  450.  
  451. - decrementYear: sender
  452. {
  453.     if (--year < 1) year = 9999;
  454.     [self _updateCalendar];
  455.     return self;
  456. }
  457.  
  458.  
  459. - takeMonthIntValueFrom: sender
  460. {
  461.     if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
  462.     if ([sender respondsTo: @selector (intValue)]) [self month: [sender intValue]];
  463.     [self _nextText: sender];
  464.     return self;
  465. }
  466.  
  467.  
  468. - takeDayIntValueFrom: sender
  469. {
  470.     if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
  471.     if ([sender respondsTo: @selector (intValue)]) [self day: [sender intValue]];
  472.     [self _nextText: sender];
  473.     return self;
  474. }
  475.  
  476.  
  477. - takeYearIntValueFrom: sender
  478. {
  479.     if ([sender isKindOf: [Matrix class]]) sender = [sender selectedCell];
  480.     if ([sender respondsTo: @selector (intValue)]) [self year: [sender intValue]];
  481.     [self _nextText: sender];
  482.     return self;
  483. }
  484.  
  485.  
  486. - showSystemDate: sender
  487. {
  488.     [self _initWithSystemDate];
  489.     [self _updateCalendar];
  490.     return self;
  491. }
  492.  
  493.  
  494. //----------------------------------------------------------------------------------------------------
  495. //    Archiving
  496. //----------------------------------------------------------------------------------------------------
  497. - read: (NXTypedStream*) stream
  498. {
  499.     [super read: stream];
  500.     NXReadTypes (stream, "iii@@@c", 
  501.         &month, &day, &year, &monthTextField, &dayMatrix, &yearTextField, &isEnabled);
  502.     return self;
  503. }
  504.  
  505.  
  506. - write: (NXTypedStream*) stream
  507. {
  508.     [super write: stream];
  509.     NXWriteTypes (stream, "iii@@@c", 
  510.         &month, &day, &year, &monthTextField, &dayMatrix, &yearTextField, &isEnabled);
  511.     return self;
  512. }
  513.  
  514.  
  515. //----------------------------------------------------------------------------------------------------
  516. //    IB Methods
  517. //----------------------------------------------------------------------------------------------------
  518. - (const char*) getInspectorClassName
  519. {
  520.     //  Returns the name of the class responsible for managing custom 
  521.     //  IB Attributes inspection.
  522.         
  523.     return "CalendarInspector";
  524. }
  525.  
  526.  
  527. - getMinSize:(NXSize *)minSize maxSize:(NXSize *)maxSize from:(int)where
  528. {
  529.     //  Constrains resize within IB.
  530.     
  531.     minSize->width = maxSize->width = 175.0;
  532.     minSize->height = maxSize->height = 137.0;
  533.     return self;
  534. }
  535.  
  536.  
  537. @end